package com.glsc.ngateway.platform.service.itflow.flowstrategy.systemNameModify;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.util.CollectionUtils;
import com.glsc.ngateway.common.api.flowable.dto.FlowCommonTaskDto;
import com.glsc.ngateway.common.api.flowable.dto.param.DoTaskCommentDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.AbstractCreateParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.AbstractFlowSearchParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.AbstractPassParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.system.SystemPassParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.system.SystemRejectParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.formData.system.SystemNameModifyTaskFormDataDto;
import com.glsc.ngateway.common.api.flowable.dto.task.formData.system.domainDto.FlowFormSystemNameModifyDto;
import com.glsc.ngateway.common.api.flowable.resp.ResponseSituationDto;
import com.glsc.ngateway.common.api.platform.dto.user.PlatformUserDto;
import com.glsc.ngateway.common.api.platform.dto.user.UserLeadersDto;
import com.glsc.ngateway.common.base.converter.platform.FlowFormSystemNameModifyConverter;
import com.glsc.ngateway.common.base.domain.mysql.gateway.oaflow.TOaFlowSyncLog;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.flow.FlowFormSystemNameModify;
import com.glsc.ngateway.common.base.enums.FlowableResponseSituationStatusEnum;
import com.glsc.ngateway.common.base.enums.OaFlowSyncLogStatusEnum;
import com.glsc.ngateway.common.base.enums.OaFlowSyncMessageDescriptionEnum;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowEnum;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowStatus;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowTaskEnum;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowTaskParamEnum;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.common.base.repo.mysql.gateway.oaflow.TOaFlowSyncLogRepository;
import com.glsc.ngateway.common.base.repo.mysql.gateway.platform.FlowFormSystemNameModifyRepo;
import com.glsc.ngateway.platform.service.FlowCommonService;
import com.glsc.ngateway.platform.service.UserService;
import com.glsc.ngateway.platform.service.itflow.flowstrategy.AbstractItFlowStrategy;
import com.glsc.ngateway.platform.service.itflow.flowstrategy.systemNameModify.taskPostHandler.ISystemNameModifyTaskPostHandlerAudit;
import com.glsc.ngateway.platform.service.itflow.flowstrategy.systemNameModify.taskPostHandler.SNMStepHandlerHelper;
import com.glsc.ngateway.platform.service.system.SystemMainService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * @author zhengyr
 * @date 2023/4/17-10:54
 */
@Component
public class SystemNameModifyFlowStrategy extends AbstractItFlowStrategy<FlowFormSystemNameModifyDto, SystemNameModifyTaskFormDataDto> {

    Logger logger = LoggerFactory.getLogger(SystemNameModifyFlowStrategy.class);

    private Map<String, ISystemNameModifyTaskPostHandlerAudit> taskPostHandlerMap;

    @Autowired
    public void setTaskPostHandlerMap(List<ISystemNameModifyTaskPostHandlerAudit> taskPostHandlerList) {
        this.taskPostHandlerMap = taskPostHandlerList.stream()
                .collect(Collectors.toMap(ISystemNameModifyTaskPostHandlerAudit::getTaskDefinitionKey, handler -> handler));
    }

    @Autowired
    private UserService userService;

    @Autowired
    private SystemMainService systemMainService;

    @Autowired
    private FlowFormSystemNameModifyRepo flowFormSystemNameModifyRepo;

    @Autowired
    private SNMStepHandlerHelper handlerHelper;

    @Autowired
    private FlowCommonService flowCommonService;

    @Override
    protected Map<String, ISystemNameModifyTaskPostHandlerAudit> getTaskPassMap() {
        return taskPostHandlerMap;
    }

    @Override
    protected Map<String, ISystemNameModifyTaskPostHandlerAudit> getTaskRejectMap() {
        return taskPostHandlerMap;
    }

    @Override
    public ITFlowEnum getFlowEnum() {
        return ITFlowEnum.SYSTEM_NAME_MODIFY;
    }

    @Autowired
    private TOaFlowSyncLogRepository logRepository;

    /**
     * 流程节点，发起时动态指定节点处理人
     */
    @Override
    protected Map<String, List<String>> initDefaultTaskUserGroup(PlatformUserDto creator) throws PlatformException {
        Map<String, List<String>> taskAssignee = new HashMap<>(8);
        //1、发起人
        taskAssignee.put(ITFlowTaskParamEnum.SYSTEM_NAME_MODIFY_FLOW_BEGIN_TASK_ASSIGNMENTS.getName(), Collections.singletonList(creator.getUsername()));
        UserLeadersDto userLeadersDto = userService.getDirectLeaderAndPrimaryDepartmentLeader(creator);
        //2、直属上级
        if (CollectionUtils.isEmpty(userLeadersDto.getDirectLeaders())) {
            throw PlatformException.error("未找到直属上级", false, false);
        }
        taskAssignee.put(ITFlowTaskParamEnum.SYSTEM_NAME_MODIFY_FLOW_LEADER_TASK_ASSIGNMENTS.getName(), userLeadersDto.getDirectLeaders());
        //3、部门领导
        if (CollectionUtils.isEmpty(userLeadersDto.getLevelOneDepLeaders())) {
            throw PlatformException.error("未找到部门领导", false, false);
        }
        taskAssignee.put(ITFlowTaskParamEnum.SYSTEM_NAME_MODIFY_FLOW_DEPARTMENT_SUPER_TASK_ASSIGNMENTS.getName(), userLeadersDto.getLevelOneDepLeaders());
        return taskAssignee;
    }

    /**
     * 流程节点其他变量
     */
    @Override
    protected Map<String, String> initDefaultTaskConditionalVar(Map<String, String> taskAssignee, PlatformUserDto createUser) {
        return new HashMap<>(0);
    }

    /**
     * 流程创建后置处理
     */
    @Override
    protected void createAfterHandle(AbstractCreateParamDto<FlowFormSystemNameModifyDto, SystemNameModifyTaskFormDataDto> param, String operator) {
        FlowFormSystemNameModifyDto formMainData = param.getFlowableFormData().getFormMainData();
        ResponseSituationDto processInfo = flowCommonService.getProcessInfo(param.getProcessId());
        //保存草稿时不需要检查
        if (StrUtil.isBlank(param.getCreateToTaskDefinitionKey())
                || !param.getCreateToTaskDefinitionKey().equals(FlowCommonTaskDto.BEGIN_TASK)) {
            //发送邮件通知下一节点审批
            handlerHelper.notify(processInfo, formMainData.getProcessTitle(), formMainData.getFlowCreateOperatorName(), formMainData.getOpNameOld(), formMainData.getOpName());
        }
    }

    @Override
    public SystemNameModifyTaskFormDataDto saveOrUpdateForm(SystemNameModifyTaskFormDataDto flowableForm) throws PlatformException {
        if (StringUtils.isNotBlank(flowableForm.getProcessId())) {
            FlowFormSystemNameModifyDto newDtoData = flowableForm.getFormMainData();
            newDtoData.setProcessId(flowableForm.getProcessId());

            FlowFormSystemNameModify oldFormMainData = flowFormSystemNameModifyRepo.findByProcessId(flowableForm.getProcessId());
            if (Objects.nonNull(oldFormMainData)) {
                newDtoData.setId(oldFormMainData.getId());
            }
            FlowFormSystemNameModify newFormMainData = FlowFormSystemNameModifyConverter.dto2Do(newDtoData);
            flowFormSystemNameModifyRepo.save(newFormMainData);
        }
        return flowableForm;
    }

    @Override
    public SystemNameModifyTaskFormDataDto findFormByProcessId(String processId) {
        FlowFormSystemNameModify formMainData = flowFormSystemNameModifyRepo.findByProcessId(processId);
        FlowFormSystemNameModifyDto formMainDataDto = FlowFormSystemNameModifyConverter.do2Dto(formMainData);
        SystemNameModifyTaskFormDataDto flowableFormData = new SystemNameModifyTaskFormDataDto();
        flowableFormData.setFormMainData(Objects.nonNull(formMainDataDto) ? formMainDataDto : new FlowFormSystemNameModifyDto());
        return flowableFormData;
    }

    /**
     * 分页查询，暂时不加
     */
    @Override
    public List<String> pageProcessId(AbstractFlowSearchParamDto param, Pageable pageable) {
        return null;
    }

    /**
     * 创建流程前检查数据
     */
    @Override
    protected <A extends AbstractCreateParamDto<FlowFormSystemNameModifyDto, SystemNameModifyTaskFormDataDto>> void checkDataBeforeCreateFlow(A taskDto) throws PlatformException {
        if (Objects.isNull(taskDto) || Objects.isNull(taskDto.getFlowableFormData()) || Objects.isNull(taskDto.getFlowableFormData().getFormMainData())) {
            throw PlatformException.error("数据为空", true, false);
        }
        checkData(taskDto.getFlowableFormData().getFormMainData());
    }

    /**
     * 审批通过前检查数据
     */
    @Override
    protected <A extends AbstractPassParamDto<FlowFormSystemNameModifyDto, SystemNameModifyTaskFormDataDto>> void checkDataBeforePassTask(A passDto) throws PlatformException {
        if (Objects.isNull(passDto) || Objects.isNull(passDto.getFlowableFormData()) || Objects.isNull(passDto.getFlowableFormData().getFormMainData())) {
            throw PlatformException.error("数据为空", true, false);
        }
        //被退回到初始节点重新提交后，仍然要检查一次
        if (passDto.getTaskDefinitionKey().equals(ITFlowTaskEnum.SYSTEM_NAME_MODIFY_FLOW_BEGIN_TASK.getTaskKey())) {
            checkData(passDto.getFlowableFormData().getFormMainData());
        }
    }

    /**
     * 检查业务表单字段
     */
    private void checkData(FlowFormSystemNameModifyDto mainData) {
        if (StringUtils.isBlank(mainData.getOpName())) {
            throw PlatformException.error("系统名称不能为空", false, false);
        }
        if (StringUtils.isBlank(mainData.getSystemClassNo())) {
            throw PlatformException.error("系统编号不能为空", false, false);
        }
        if (systemMainService.countByOpName(mainData.getOpName()) > 0) {
            throw PlatformException.error(String.format("已存在[\"%s\"]系统，请重新命名", mainData.getOpName()), false, false);
        }
        List<FlowFormSystemNameModify> existList = flowFormSystemNameModifyRepo.findAllByOpNameAndIdNotAndStatusNot(mainData.getOpName(), mainData.getId(), ITFlowStatus.UNUSUAL.getCode());
        if (CollectionUtil.isNotEmpty(existList)) {
            //是否有非强制终止的流程
            AtomicBoolean hasUnusual = new AtomicBoolean(false);
            //再去查flowable，是否被强制取消
            existList.forEach(e -> {
                ResponseSituationDto processInfo = flowCommonService.getProcessInfo(e.getProcessId());
                if (Objects.nonNull(processInfo) && FlowableResponseSituationStatusEnum.UNUSUAL.getCode().equals(processInfo.getStatus())) {
                    e.setStatus(ITFlowStatus.UNUSUAL.getCode());
                    flowFormSystemNameModifyRepo.save(e);
                } else {
                    hasUnusual.set(true);
                }
            });
            if(hasUnusual.get()) {
                throw PlatformException.error(String.format("已有流程变更系统名称为[\"%s\"]，请重新命名", mainData.getOpName()), false, false);
            }
        }
    }

    /**
     * 生成审批要素str，供同步oa使用
     */
    @Override
    public String toSpys(SystemNameModifyTaskFormDataDto formData) {
        StringBuffer sb = new StringBuffer();
        FlowFormSystemNameModifyDto formMainData = formData.getFormMainData();
        sb.append("编号:").append(formMainData.getSystemClassNo()).append("<br/>");
        sb.append("系统名称（变更前）:").append(formMainData.getOpNameOld()).append("<br/>");
        sb.append("系统名称（变更后）:").append(formMainData.getOpName()).append("<br/>");
        return sb.toString();
    }


    /**
     * 从oa侧同步流程
     */
    @Override
    public void syncFromOaFlow(TOaFlowSyncLog log, String description) throws Exception {
        OaFlowSyncMessageDescriptionEnum desc = OaFlowSyncMessageDescriptionEnum.valuesOf(description)
                .orElseThrow(() -> PlatformException.error("无法从oa同步，message.desc:" + description));
        SystemNameModifyTaskFormDataDto formData = findFormByProcessId(log.getProcessId());
        //目前oa侧是单节点流程，会受到两条消息，第二条是流程结束，不需要处理
        if (desc.isEnd()) {

        } else if (desc.isPass()) {
            //通过
            SystemPassParamDto<FlowFormSystemNameModifyDto, SystemNameModifyTaskFormDataDto> passParam = new SystemPassParamDto<>();
            passParam.setProcessId(log.getProcessId());
            passParam.setTaskId(log.getTaskId());
            passParam.setCommentDto(DoTaskCommentDto.builder().fullMessage("【oa端审批通过】").build());
            passParam.setProcessDefinitionKey(log.getProcessDefKey());
            passParam.setTaskDefinitionKey(log.getTaskDefKey());
            passParam.setProcessTitle(formData.getProcessTitle());
            passParam.setFlowableFormData(formData);
            taskAuditPass(passParam, log.getApproved(), () -> {
                logRepository.updateStatusAndRequestId(log.getWorkflowId(), log.getTaskId(), log.getRequestId(), OaFlowSyncLogStatusEnum.END_OA.getCode());
                auditPassAfterHandle(passParam, log.getApproved());
            });
        } else if (desc.isReject()) {
            //拒绝，跳到上一步
            SystemRejectParamDto rejectParam = new SystemRejectParamDto();
            rejectParam.setProcessId(log.getProcessId());
            rejectParam.setTaskId(log.getTaskId());
            rejectParam.setProcessDefinitionKey(log.getProcessDefKey());
            rejectParam.setTaskDefinitionKey(log.getTaskDefKey());
            rejectParam.setProcessTitle(formData.getProcessTitle());
            //回退至上一步
            ITFlowTaskEnum task = ITFlowEnum.findTask(log.getProcessDefKey(), log.getTaskDefKey());
            if (Objects.isNull(task.getPrevTask())) {
                throw PlatformException.error(String.format("当前无可回退节点:%s, %s", log.getProcessDefKey(), log.getTaskDefKey()));
            }
            rejectParam.setTargetTask(task.getPrevTask().getTaskKey());
            rejectParam.setCommentDto(DoTaskCommentDto.builder().fullMessage("【oa端审批驳回】").build());
//            reject(rejectParam, log.getApproved(), () -> {
//                logRepository.updateStatusAndRequestId(log.getWorkflowId(), log.getTaskId(), log.getRequestId(), OaFlowSyncLogStatusEnum.END_OA.getCode());
//                auditRejectAfterHandle(rejectParam, log.getApproved());
//            });
        }
    }
}
